package com.saturn.titan.core.repository.mybatis.interceptor;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Properties;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import com.saturn.titan.core.bean.base.DatabaseBean;
import com.saturn.titan.core.exception.DataHaveChangedException;
import com.saturn.titan.core.repository.mybatis.annotations.GenerateDao;
import com.saturn.titan.core.repository.mybatis.base.BaseDao;
import com.saturn.titan.core.repository.mybatis.base.BaseDaoSqlProvider;

/**
 * mybatis 拦截器，用来实现通用接口，以及更新的version校验
 * @author Administrator
 *
 */
@Intercepts({
		@Signature(type = Executor.class, method = "query", args = {
				MappedStatement.class, Object.class, RowBounds.class,
				ResultHandler.class }),
		@Signature(type = Executor.class, method = "update", args = {
				MappedStatement.class, Object.class }) })
public class BaseDaoInterceptor implements Interceptor {

	private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
	private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();

	public Object intercept(Invocation invocation) throws Throwable {

		Object[] queryArgs = invocation.getArgs();

		final MappedStatement ms = (MappedStatement) queryArgs[0];
		boolean isVersionValid = false;
		if(isBaseDaoMethod(ms.getId())){
			// 获取查询的dao class对象
			Class<?> dao = getTargetDaoClass(ms.getId());
			// 获取dao被调用的方法对象
			// Method method = getTargetDaoMethodName(ms.getId(),dao,parameter);
			
			//从标注中，获取方法需要返回什么类型参数
			Class<?> beanType = getBeanType(dao);
			
			Object param = queryArgs[1];
			//如果传入参数类型，Databasebean的子类，则设置本身为返回类型
			if(param != null){
				Class<?> tmpClass = param.getClass().getSuperclass();
				while(tmpClass != Object.class){
					if(tmpClass == DatabaseBean.class){
						beanType = param.getClass();
						break;
					}
					tmpClass = tmpClass.getSuperclass();
				}
			}
			
			// 告诉SqlProvider按照指定类型实体生成sql
			BaseDaoSqlProvider.currentResultBeanClass.set(beanType);
			// 指定mybits将结果集转换的类型
			setResultType(ms, beanType);
			
			
			
			isVersionValid = isVersionValid(param,ms.getSqlCommandType());
		}
		
		Object result = invocation.proceed();
		
		if(result instanceof Integer && isVersionValid && ((Integer)result) == 0){
			throw new DataHaveChangedException();
		}
		return result;
	}

	private boolean isVersionValid(Object param, SqlCommandType sqlCommandType) throws Exception{
		
		if(sqlCommandType.equals(SqlCommandType.UPDATE)){
			if(param instanceof DatabaseBean){
				Method method = DatabaseBean.class.getMethod("getVersion");
				Object versionValue = method.invoke(param);
				if(versionValue != null){
					return true;
				}
			}
			if(param instanceof Map){
				@SuppressWarnings("rawtypes")
				Map p = (Map)param;
				for(Object key : p.keySet()){
					param = p.get(key);
					
					if(param instanceof DatabaseBean){
						Method method = DatabaseBean.class.getMethod("getVersion");
						Object versionValue = method.invoke(param);
						if(versionValue != null){
							return true;
						}
					}
				}
			}
		}
		return false;
	}

	/**
	 * 判断是否是通用接口方法
	 * @param method
	 * @return
	 */
	private boolean isBaseDaoMethod(String method) {
		
		return BaseDao.baseDaoMethods.contains(method.substring(method.lastIndexOf(".") + 1));
	}

	/**
	 * 根据公共接口CLass对象获取公共接口的实体类型
	 * 
	 * @param dao
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 */
	private Class<?> getBeanType(Class<?> dao) throws Exception {
		return dao.getAnnotation(GenerateDao.class).beanType();
	}

	private Class<?> getTargetDaoClass(String id) {
		try {
			return Class.forName(id.substring(0, id.lastIndexOf(".")));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}

	private void setResultType(MappedStatement ms, Class<?> clazz) {

		if (ms.getId().endsWith("deleteById") || ms.getId().endsWith("delete")
				|| ms.getId().endsWith("update")
				|| ms.getId().endsWith("updateColumns")) {
			clazz = Integer.class;
		}
		
		if (ms.getId().endsWith("selectCount")) {
			clazz = Long.class;
		}
		
		if (ms.getId().endsWith("insert")) {
			clazz = void.class;
		}

		ResultMap resultMap = ms.getResultMaps().get(0);
		MetaObject metaObject = forObject(resultMap);
		metaObject.setValue("type", clazz);
	}

	public static MetaObject forObject(Object object) {
		return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY,
				DEFAULT_OBJECT_WRAPPER_FACTORY);
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {

	}

}
